'use strict';

const wx_server_sdk = require('wx-server-sdk');
wx_server_sdk.init({
    env: 'shifu-map-tvsh8'
    // env: 'other'
});

const db = wx_server_sdk.database({ throwOnNotFound: false });
const dbCommand = db.command;

//物联网通讯协议:mqtt
const asyncMqtt = require('async-mqtt');

var nowTime = null;
var oriGpsData = require('./gpsRecord');
oriGpsData = oriGpsData.data;

//云函数入口
async function funcMain(event, context) {
    context.callbackWaitsForEmptyEventLoop = false; //针对node 10.15版本的设置,避免一些函数跑空造成拖延时间
    nowTime = getBeijingTime();

    /*
    {
        //数据库初始化:先在数据库里保存各GPS数据的第一版记录
    

        console.log('oriGpsData长度', oriGpsData.length);
        //长度必须大于50*2

        try {
            await db.createCollection('IoTGps');
        } catch (e) {
            console.log(e);
        }
    

        //清空数据
        await db.collection('IoTGps').where({
            _id: dbCommand.neq('999')
        }).remove();

        var deviceName_head = 'gps_',
            positionIndex = 0;

        for (var i = 1; i < 51; i++) {
            //50号车在0号位,49号车在2号位,1号车在98号位
            //规律为:第n个GPS,初始位置为(50-n)*2
            positionIndex = (50 - i) * 2;
            // schoolBusData
            await db.collection('IoTGps').add({
                data: {
                    deviceName: deviceName_head + i,

                    beijingTs: nowTime.ts,
                    beijingTime: nowTime.time,
                    positionIndex,

                    longitude: oriGpsData[positionIndex].longitude,
                    latitude: oriGpsData[positionIndex].latitude,
                    direction: oriGpsData[positionIndex].direction,
                }
            })
        }

        return;
    }
    */

    console.log(nowTime.time, '函数传入的事件event', event);
    if (event.hasOwnProperty('payload')) {
        //有payload字段,则为物联网GPS设备上报的数据,经规则引擎转发到了本云函数
        var payload = event.payload;

        //将原纪录赋值给lastPosition变量
        var lastPosition = await db.collection('IoTGps').where({
            deviceName: payload.deviceName
        }).get();
        lastPosition = lastPosition.data[0];
        //删除原有的记录
        delete lastPosition.lastPosition;

        //将新数据和上次数据更新到此GPS的数据文档中
        var saveDataToDb = await db.collection('IoTGps').where({
            deviceName: payload.deviceName
        }).update({
            data: {
                positionIndex: payload.positionIndex,

                beijingTime: nowTime.time,
                beijingTs: nowTime.ts,

                longitude: oriGpsData[payload.positionIndex].longitude,
                latitude: oriGpsData[payload.positionIndex].latitude,
                direction: oriGpsData[payload.positionIndex].direction,

                lastPosition //这是"lastPosition:lastPosition"的简写
            }
        })

    } else if (event.hasOwnProperty('Type') && event.Type == 'timer' &&
        event.hasOwnProperty('TriggerName') && event.TriggerName == 'every30s') {
        //对于触发器启动的本函数,只执行批量生成GPS设备数据功能
        await batch_gpsSendData();

    } else if (event.hasOwnProperty('actionType') && event.actionType == '刷新') {
        //返回所有有deviceName字段的记录.目前本功能暂无用处

        var gpsArray = await db.collection('IoTGps').where({
            deviceName: dbCommand.exists(true)
        }).field({
            _id: false,
            beijingTs: false,
        }).get();
        console.log('gpsArray', gpsArray.data.length);
        return gpsArray.data;
    }

    return;
}

//计算下一次GPS的位置编号
function getNextPositionIndex(nowIndex, positionLength) {
    //若到了最后一个GPS坐标节点,则跳回到第一个节点
    return nowIndex + 1 > positionLength - 1 ? nowIndex + 1 - positionLength : nowIndex + 1;
}

//批量虚拟GPS设备上报数据
async function batch_gpsSendData() {
    //本工程仅启用1号GPS设备动态发送数据,在此先从数据库读取上次所在的位置
    var gps_1_positionIndex = await db.collection('IoTGps').where({
        deviceName: 'gps_1'
    }).field({
        _id: false,
        positionIndex: true,
    }).get();
    //当前1号GPS所在的点编号
    console.log('当前1号车所在位置', gps_1_positionIndex);
    //仅保留positionIndex,其余数据都舍弃
    gps_1_positionIndex = gps_1_positionIndex.data[0].positionIndex;

    //生成各GPS下一站的编号
    var nextPositionArray = [];
    var devices = require('./devices');//devices.json文件因包含密钥,在开源工程中未上传,其数据格式为:
    //{"data": [{"deviceName": "gps_1","secret": "******"}]}
    devices = devices.data;//精简数据层级

    //推算新的1号点位编号
    devices[0].positionIndex = getNextPositionIndex(gps_1_positionIndex, oriGpsData.length);
    // for (var i = 1; i < devices.length; i++) {
    //     devices[i].positionIndex = getNextPositionIndex(devices[i - 1].positionIndex, oriGpsData.length);
    // }

    //批量向物联网平台发送自己的位置编号(而不是坐标).
    //接收数据的代码,会根据位置编号,去库里找坐标,给安上

    // for (var i = 0; i < devices.length; i++) {
    //只做1号设备
    for (var i = 0; i < 1; i++) {
        var deviceInfo = gnrtMqttUsernamePassword({
            productId: 'U8YREXG2GG',
            deviceName: devices[i].deviceName,
            secret: devices[i].secret,
            positionIndex: devices[i].positionIndex,
        })

        await mqttUploadData(deviceInfo);
    }

    return;
}

//根据设备信息,计算生成联网通讯的验证密码
function gnrtMqttUsernamePassword(deviceInfo) {
    // 产生随机数的函数(云函数已内置uuid包,可以不用在package.json中声明即可直接引用)
    const uuid = require('uuid');
    // 1. 生成 connid 为一个随机字符串，方便后台定位问题
    var connid = uuid.v4().replace(/-/g, '').slice(0, 5); //长度为5
    // console.log('connid', connid);

    // 2. 生成过期时间，表示签名的过期时间,从纪元1970年1月1日 00:00:00 UTC 时间至今秒数的 UTF8 字符串
    var expiry = Math.round(nowTime.ts / 1000) + 3600 * 24;//有效期为24小时
    // var expiry = '1648852183';
    // console.log('expiry', expiry);

    // 3. 生成 MQTT 的 clientid 部分, 格式为 ${productid}${devicename}
    var clientId = deviceInfo.productId + deviceInfo.deviceName;
    // console.log('clientId', clientId);
    deviceInfo.clientId = clientId;

    // 4. 生成 MQTT 的 username 部分, 格式为 ${clientid};${sdkappid};${connid};${expiry}
    var username = `${clientId};12010126;${connid};${expiry}`;
    // console.log('username', username);

    //5.  对 username 进行签名，生成token、根据物联网通信平台规则生成 password 字段
    const crypto = require('crypto-js');
    const rawKey = crypto.enc.Base64.parse(deviceInfo.secret); // 对设备密钥进行base64解码
    const token = crypto.HmacSHA256(username, rawKey);
    const password = token.toString(crypto.enc.Hex) + ";hmacsha256";
    // console.log('password', password);

    deviceInfo.username = username;
    deviceInfo.password = password;
    //证书认证型端口：8883；密钥认证型：1883
    deviceInfo.port = '1883';
    deviceInfo.protocol = 'mqtt';
    //广州域设备填入：PRODUCT_ID.iotcloud.tencentdevices.com，
    deviceInfo.host = deviceInfo.productId + '.iotcloud.tencentdevices.com';

    // console.log('更新后的deviceInfo', deviceInfo);

    //属性规则:https://www.npmjs.com/package/mqtt#connect
    return deviceInfo;
}

//基于mqtt物联网协议,向腾讯云物联网通信IoT Hub发送数据
async function mqttUploadData(deviceInfo) {
    var client = asyncMqtt.connect(deviceInfo);

    console.log("连接" + deviceInfo.deviceName);
    try {
        var topic = deviceInfo.productId + '/' + deviceInfo.deviceName + '/event';

        var payloadJson = {
            deviceName: deviceInfo.deviceName,
            positionIndex: deviceInfo.positionIndex,
            // longitude: 1,
            // latitude: 2
        }

        await client.publish(topic, JSON.stringify(payloadJson), { qos: 1 });
        await client.end();
        console.log("关闭" + deviceInfo.deviceName);
    } catch (ex) {
        console.log("连接物联网平台出错", ex.stack);
    }
    return;
}

//获取当前时间戳
function getNowTimestamp() {
    return new Date().getTime();
}

//生成时间数据包
function getBeijingTime(localTs) {
    var time_ts = localTs;
    if (typeof(localTs) != 'number' || localTs == 0) {
        time_ts = getNowTimestamp();
    }

    var time = tsToDateStr(time_ts);

    return {
        ts: time_ts,
        time,
    };
}

//将时间戳生成北京时间的字符串时间
function tsToDateStr(ts_para) {
    //在此弥补时差
    var time_ts = new Date(ts_para + 8 * 3600000);

    // console.log('用于制作中文的ts', time_ts.getTime());

    var time_str = time_ts.getFullYear() + '-';

    if ((time_ts.getMonth() + 1) < 10) {
        time_str += '0';
    }
    time_str += '' + (time_ts.getMonth() + 1) + '-';


    if (time_ts.getDate() < 10) {
        time_str += '0';
    }
    time_str += '' + time_ts.getDate() + ' ';


    if (time_ts.getHours() < 10) {
        time_str += '0';
    }
    time_str += '' + time_ts.getHours() + ':';

    if (time_ts.getMinutes() < 10) {
        time_str += '0';
    }
    time_str += '' + time_ts.getMinutes() + ':';

    if (time_ts.getSeconds() < 10) {
        time_str += '0';
    }
    time_str += '' + time_ts.getSeconds(); // + ':' + time_ts.getMilliseconds();

    return time_str;
}


exports.main = funcMain